package ru.picman;

import ru.picman.ui.Paintable;
import ru.picman.util.EasyMath;
import ru.picman.util.GreenThread;
import android.graphics.Point;
import android.graphics.PointF;

/**
 * This is the root class of all the objects you can interact with
 * in the game. This includes the playable characters (the birds),
 * the non-playable characters (the pigs) and the items.
 * 
 * @author Matthijs
 */
public abstract class Entity
{
	
	/**
	 * Constructs a new Entity instance. The entity will be of the
	 * specified type, which cannot be changed. The default position
	 * will be (0,0).
	 * 
	 * @param type The type of the entity
	 * @see #Entity(EntityType, Point) Entity(EntityType, Point)
	 */
	public Entity(EntityType type) {
		this(type,new Point());
	}
	
	/**
	 * Constructs a new Entity instance. The entity will be of the
	 * specified type, which cannot be changed. The entity location
	 * will be at <i>position</i>.
	 * 
	 * @param type The type of the entity
	 * @param position The position of the entity
	 * @see #Entity(EntityType) Entity(EntityType)
	 */
	public Entity(EntityType type, Point position) {
		this.position = new PointF(position);
		this.type = type;
		setVisible(true);
	}
	
	//
//	06-25 14:58:17.976: INFO/System.out(583): (1.0, 3.3599997) and (1.0, 3.0)

	/**
	 * Checks if the entity collides with an other entity. Assumes 
	 * each entity is the same size, being the tile size, and the
	 * entity is straight, i.e.: not rotated (so |_| and not \_\)
	 * 
	 * @param other The other entity
	 * @return true if it collides, false otherwise
	 */
	public boolean collides(Entity other) {
		return ((EasyMath.isBetweenIncl(getX(),other.getX(),other.getX()+1)
			  || EasyMath.isBetweenIncl(other.getX(),getX(),getX()+1))
			&& ((EasyMath.isBetweenIncl(getY(),other.getY(),other.getY()+1)
		      || EasyMath.isBetweenIncl(other.getY(),getY(),getY()+1))));
	}
	
	/**
	 * Starts the AI thread of this entity. The thread
	 * will be added to the gameloop's thread processor
	 * and started.
	 * 
	 * @throws NullPointerException If the entity is not
	 *             added to a world
	 */
	public void startAIThread() {
		world.getGame().getGameLoop().addThread(aiThread);
	}
	
	/**
	 * Stops the AI thread of this entity. The thread will
	 * be removed from the gameloop's thread processor automatically.
	 */
	public void stopAIThread() {
		aiThread.stop();
	}
	
	/**
	 * Sets the AI thread of this entity.
	 * 
	 * @param ai The AI thread
	 * @throws IllegalStateException If this entity already has an
	 *             AI thread running
	 */
	public void setAIThread(GreenThread ai) {
		if (aiThread != null && aiThread.isRunning())
			throw new IllegalStateException("Already running an AI thread");
		this.aiThread = ai;
	}
	
	/**
	 * Returns the set AI thread.
	 * 
	 * @return The AI thread of this entity.s
	 */
	public GreenThread getAIThread() {
		return aiThread;
	}
	
	/**
	 * Sets the position of this entity to Point p.
	 * 
	 * @param p The desired position
	 */
	public void setPosition(Point p) {
		setPosition(p.x,p.y);
	}
	
	/**
	 * Sets the position of this entity to Point p.
	 * 
	 * @param p The desired position
	 */
	public void setPosition(PointF p) {
		setPosition(p.x,p.y);
	}
	
	/**
	 * Sets the position of this entity to (x,y).
	 * 
	 * @param x The desired x position
	 * @param y The desired y position
	 */
	public void setPosition(float x, float y) {
		this.position.set(x,y);
	}
	
	/**
	 * Aligns the current position of the entity to the closest tile.
	 */
	public void alignPosition() {
		position.set((int)(position.x + .5f),(int)(position.y + .5f));
	}
	
	/**
	 * Sets the x position of this entity to the given x.
	 * 
	 * @param x The new x position
	 */
	public void setX(float x) {
		this.position.x = x;
	}
	
	/**
	 * Sets the y position of this entity to the given y.
	 * 
	 * @param y The new y position
	 */
	public void setY(float y) {
		this.position.y = y;
	}
	
	/**
	 * Returns the current position in an Integer format.
	 * 
	 * @return The current position, rounded to two integers
	 */
	public Point getPositionI() {
		return new Point(Math.round(position.x),Math.round(position.y));
	}
	
	/**
	 * Returns the current position.
	 * 
	 * @return The current position
	 */
	public PointF getPositionF() {
		return new PointF(position.x,position.y);
	}
	
	/**
	 * Returns the x position.
	 * 
	 * @return The x position
	 */
	public float getX() {
		return position.x;
	}
	
	/**
	 * Returns the y position.
	 * 
	 * @return The y position
	 */
	public float getY() {
		return position.y;
	}
	
	/**
	 * Sets this entity to visible or invisible.
	 * 
	 * @param vis true to make it visible
	 */
	public void setVisible(boolean vis) {
		this.visible = vis;
	}
	
	/**
	 * Checks if this entity is visible.
	 * 
	 * @return true if it is
	 */
	public boolean isVisible() {
		return visible;
	}
	
	/**
	 * Returns the Type this Entity is of.
	 * 
	 * @return The EntityType
	 */
	public EntityType getType() {
		return type;
	}
	
	/**
	 * Sets the painter of this Entity to the specified
	 * Paintable. The Paintable should paint this object.
	 * 
	 * @param p The Paintable
	 */
	public void setPainter(Paintable p) {
		this.painter = p;
	}
	
	/**
	 * Returns the set painter.
	 * 
	 * @return The painter, as a Paintable
	 */
	public Paintable getPainter() {
		return painter;
	}
	
	/**
	 * Returns the world this entity is in.
	 * 
	 * @return The world
	 */
	public World getWorld() {
		return world;
	}
	
	/**
	 * Sets the world this entity is in. This method should 
	 * <b>not</b> be called manually, as the World class handles
	 * it.
	 * 
	 * @param world The world this Entity is in
	 */
	protected void setWorld(World world) {
		this.world = world;
	}
	
	//
	
	protected World         world;
	protected PointF        position;
	
	private GreenThread     aiThread;
	private EntityType      type;
	private Paintable       painter;
	
	private boolean 		visible;
	
	public static final int MOVE_DELAY = 50;
	
}
